﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Media;
using SharpMap.Geometries;
using SharpMap.Layers;
using SharpMap.Presentation.Presenters;
using SharpMap.Presentation.Views;
using SharpMap.Rendering;
using SharpMap.Rendering.Rendering2D;
using SharpMap.Rendering.Thematics;
using SharpMap.Styles;
using SharpMap.Wpf.Rendering;
using GeoPoint = SharpMap.Geometries.Point;
using SharpMap.Data;
using System.Collections;
namespace SharpMap.Presentation.Wpf
{
    /// <summary>
    /// Provides the input-handling and view-updating logic for a 2D map view.
    /// </summary>
    /// <remarks>
    /// ViewModel name is used instead of MapPresenter for
    /// <see cref="MapPresenter2D">SharpMap.MapPresenter</see> to avoid confusion with
    /// WPF presenters
    /// </remarks>
    public class MapViewModel : MapPresenter2D, INotifyPropertyChanged, IViewContext
    {

        //-----------------------------------------------------
        //
        //  Construction
        //
        //-----------------------------------------------------

        #region Construction
        public MapViewModel(Map map, IMapView2D mapView)
            : base(map, mapView)
        {

            _renderedObjects = new MapLayerViewModel(this);
            _renderedAdorners = new MapLayerViewModel(this);
            RenderedSelectedObjects = new MapLayerViewModel(this);
            RenderedHighlightedObjects = new MapLayerViewModel(this);

            wireupExistingLayers(Map.Layers);
            Selection.SelectionChanged += new EventHandler(Selection_SelectionChanged);

            Map.Layers.ListChanged += new ListChangedEventHandler(layersListChanged);
           
        }

        internal MapViewModel(IMapView2D mapView)
            : this(new Map(), mapView)
        {

        } 
        #endregion

        //-----------------------------------------------------
        //
        //  MapPresenter2D Overrides
        //
        //-----------------------------------------------------

        #region MapPresenter2D Overrides

        #region MapPresenter2D Abstract methods implemntations
        protected override IVectorRenderer2D CreateVectorRenderer()
        {
            if (_vectorRenderer == null)
            {
                 _vectorRenderer=  new SharpMap.Wpf.Rendering.VectorRenderer();
            }
            return _vectorRenderer;
        }

        protected override IRasterRenderer2D CreateRasterRenderer()
        {
            return new RasterRenderer<Drawing>();
        }

        protected override ITextRenderer2D CreateTextRenderer()
        {
            return new SharpMap.Wpf.Rendering.TextRenderer();
        }

        protected override Type GetRenderObjectType()
        {
            return typeof(Drawing);
        }
        
        #endregion


        /// <summary>
        /// 
        /// </summary>
        /// <remarks></remarks>
        /// <param name="layer">Layer to be rendered</param>
        protected override void RenderFeatureLayer(IFeatureLayer layer)
        {
            IFeatureRenderer renderer = GetRenderer<IFeatureRenderer>(layer);

            Debug.Assert(renderer != null);

            IEnumerable<FeatureDataRow> features
                = layer.Features.Select(ViewEnvelopeInternal.ToGeometry());

            Debug.Assert(layer.Style is VectorStyle);
            VectorStyle style = layer.Style as VectorStyle;

            

            RenderFeatures(renderer, style, features, RenderState.Normal);
            RenderFeatures(renderer, style, layer.SelectedFeatures, RenderState.Selected);
            RenderFeatures(renderer, style, layer.HighlightedFeatures, RenderState.Highlighted);
  
        }


   


        protected override void OnRenderingLayer(ILayer layer)
        {
            base.OnRenderingLayer(layer);
        }

        protected override void OnRenderedLayer(ILayer layer)
        {
            base.OnRenderedLayer(layer);
        }

        protected override void CreateGeometryRenderer(Type renderObjectType)
        {
            if (_featureRenderer != null)
            {
                Debug.WriteLine("FeatureRenderer recreated. Should this happen??");
                _featureRenderer.Dispose();
            }
            _featureRenderer = new FeatureRenderer(VectorRenderer);
            RegisterRendererForLayerType(typeof(GeometryLayer), _featureRenderer);
        }

        protected override void OnRenderingAllLayers()
        {
            RenderedObjects.Clear();
            RenderedSelectedObjects.Clear();
            RenderedHighlightedObjects.Clear();
        }

        protected override void OnRenderingSelection()
        {
            RenderingSelection = true;
        }

        protected override void OnRenderedSelection()
        {
            RenderingSelection = false;
        }
        
        protected override void OnViewMatrixChanged()
        {
            if (IsViewMatrixInitialized)
            {
                RaisePropertyChanged("GeoCenter");
                RaisePropertyChanged("ToViewTransform");
                RaisePropertyChanged("ViewEnvelope");
                RaisePropertyChanged("WorldWidth");
                RaisePropertyChanged("WorldHeight");

            }
        }

        protected override void OnViewMatrixInitialized()
        {
            RaisePropertyChanged("ToViewTransform");
  
        }
 
        protected override void SetViewEnvelope(BoundingBox fromEnvelope, BoundingBox toEnvelope)
        {
            base.SetViewEnvelope(fromEnvelope, toEnvelope);
            RaisePropertyChanged("GeoCenter");
            RaisePropertyChanged("ViewEnvelope");
            RaisePropertyChanged("WorldWidth");
            RaisePropertyChanged("WorldHeight");
        }

        protected override void SetViewGeoCenter(Point fromGeoPoint, Point toGeoPoint)
        {
            RaisePropertyChanged("GeoCenter");
            RaisePropertyChanged("ViewEnvelope");
        }

        protected override void SetViewBackgroundColor(StyleColor fromColor, StyleColor toColor)
        {
            //base.SetViewBackgroundColor(fromColor, toColor);
            if (fromColor != toColor)
            {
                BackgroundColorInternal = toColor;
                RaisePropertyChanged("BackgroundColor");
            }
        }


        #endregion //MapPresenter2D

        //-----------------------------------------------------
        //
        //  MapViewControl accessible members
        //
        //-----------------------------------------------------

        #region MapViewControl/LayerView accessible members

        internal ITheme Theme
        {
            get
            {
                if (_featureRenderer != null)
                {
                    return _featureRenderer.Theme;
                }
                return null;
            }
            set 
            {
                IFeatureRenderer renderer = GetRenderer<IFeatureRenderer, GeometryLayer>();
                if (renderer != null)
                {
                    IWpfTheme wpfTheme = value as IWpfTheme;
                    if (wpfTheme != null)
                    {
                        wpfTheme.ViewContext = this;
                    }
                    renderer.Theme = value; 

                    RaisePropertyChanged("Theme");
                }
            }
        }

        public MapLayerViewModel RenderedAdorners
        {
            get { return _renderedAdorners; }
            set { _renderedAdorners = value; }
        }

        public MapLayerViewModel RenderedObjects
        {
            get { return _renderedObjects; }
            set { _renderedObjects = value; }
        }

        public Point GeoCenter
        {
            get { return GeoCenterInternal; }
            set {  GeoCenterInternal = value; }

        }

        public Matrix2D ToViewTransform
        {
            get { return ToViewTransformInternal; }
        }

        public BoundingBox ViewEnvelope
        {
            get { return ViewEnvelopeInternal; }
            set {  ViewEnvelopeInternal = value; }
        }


        public Double WorldWidth
        {
            get { return WorldWidthInternal; }
        }

        public StyleColor BackgroundColor
        {
            get { return BackgroundColorInternal; }
            set 
            { 
                BackgroundColorInternal = value;
                RaisePropertyChanged("BackgroundColor");
            }
        }

        internal Double MaximumWorldWidth
        {
            get { return MaximumWorldWidthInternal; }
        }

        public Double MinimumWorldWidth
        {
            get { return MinimumWorldWidthInternal; }
            set { MinimumWorldWidthInternal = value; RaisePropertyChanged("MinimumWorldWidth"); }
        }

        internal Double PixelWorldWidth
        {
            get { return PixelWorldWidthInternal; }
        }

        internal Double PixelWorldHeight
        {
            get { return PixelWorldHeightInternal; }
        }

        internal ViewSelection2D Selection
        {
            get { return SelectionInternal; }
        }

        internal Matrix2D ToWorldTransform
        {
            get { return ToWorldTransformInternal; }
        }

        internal Point2D ToView(Point point)
        {
            return ToViewInternal(point);
        }

        internal Point2D ToView(Double x, Double y)
        {
            return ToViewInternal(x, y);
        }

        internal Point ToWorld(Point2D point)
        {
            return ToWorldInternal(point);
        }

        internal Point ToWorld(Double x, Double y)
        {
            return ToWorldInternal(x, y);
        }

        public Double WorldAspectRatio
        {
            get { return WorldAspectRatioInternal; }
            set { WorldAspectRatioInternal = value; RaisePropertyChanged("WorldAspectRatio"); }
        }

        public Double WorldHeight
        {
            get { return WorldHeightInternal; }
        }

        internal Double WorldUnitsPerPixel
        {
            get { return WorldUnitsPerPixelInternal; }
        }
 
        internal void ShowRenderedObjects(IEnumerable<Drawing> renderedObjects)
        {
            if (RenderingSelection)
            {
                RenderedAdorners.Clear();
                foreach (Drawing d in renderedObjects)
                {
                    RenderedAdorners.Add(d);
                }
            }
            else
            {
                foreach (Drawing d in renderedObjects)
                {
                    RenderedObjects.Add(d);
                }
            }
        }

        void RenderFeatures(IFeatureRenderer renderer, VectorStyle style,IEnumerable<FeatureDataRow> features, RenderState state)
        {
            ICollection<Drawing> targetDrawings;
            switch (state)
            {
                case RenderState.Normal:
                    targetDrawings = RenderedObjects;
                    break;
                case RenderState.Selected:
                    targetDrawings = RenderedSelectedObjects;
                    break;
                case RenderState.Highlighted:
                    targetDrawings = RenderedHighlightedObjects;
                    break;
                default:
                    targetDrawings = RenderedObjects;
                    break;
            }
            foreach (FeatureDataRow feature in features)
            {
                IEnumerable<Drawing> renderedFeature =
                     (IEnumerable<Drawing>)renderer.RenderFeature(feature, style, state);

                foreach (Drawing d in renderedFeature)
                {
                    targetDrawings.Add(d);
                }
            }
        }

 
        internal void ZoomByFactor( Double zoomFactor)
        {
            

            SharpMap.Geometries.Point oldCenter = GeoCenter;
            BoundingBox newEnvelope = new BoundingBox(
                                            new Point(ViewEnvelope.LowerLeft.X * zoomFactor,
                                                               ViewEnvelope.LowerLeft.Y * zoomFactor),
                                            new Point(ViewEnvelope.UpperRight.X * zoomFactor,
                                                               ViewEnvelope.UpperRight.Y * zoomFactor));

            ZoomToWorldBoundsInternal(newEnvelope);
            //GeoCenter = oldCenter;
        }

        internal void HandleViewOffsetChangeRequestedWorkoround(object sender, MapViewPropertyChangeEventArgs<Point2D> e)
        {
            Point2D diffV = e.RequestedValue;
            Double diffWX = diffV.X * ToWorldTransform.M11 + diffV.Y * ToWorldTransform.M12;
            Double diffWY = diffV.X * ToWorldTransform.M21 + diffV.Y * ToWorldTransform.M22;
            GeoCenterInternal = new GeoPoint(GeoCenter.X + diffWX, GeoCenter.Y + diffWY);
        }


        #endregion

        //-----------------------------------------------------
        //
        //  INotifyPropertyChanged Members
        //
        //-----------------------------------------------------
        
        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(String propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                PropertyChangedEventArgs args =
                    new PropertyChangedEventArgs(propertyName);
                handler(this, args);
            }
        }
        #endregion

        //-----------------------------------------------------
        //
        //  Private Properties/Methods
        //
        //-----------------------------------------------------

        #region Private Properties/Methods

         bool RenderingSelection
        {
            get { return _renderingSelection; }
             set { _renderingSelection = value; }
        }
        public MapLayerViewModel RenderedSelectedObjects
        {
            get { return _renderedSelectedObjects; }
            private set { _renderedSelectedObjects = value; }
        }
        public MapLayerViewModel RenderedHighlightedObjects
        {
            get { return _renderedHighlightedObjects; }
            private set { _renderedHighlightedObjects = value; }
        }

        private new SharpMap.Wpf.Rendering.VectorRenderer VectorRenderer
        {
            get
            {
                return base.VectorRenderer as SharpMap.Wpf.Rendering.VectorRenderer;
            }
        }

        void Selection_SelectionChanged(object sender, EventArgs e)
        {
            if (Selection.IsEmpty)
            {
                this.RenderedAdorners.Clear();
            }
        } 
        #endregion

        //-----------------------------------------------------
        //
        //  Fields
        //
        //-----------------------------------------------------

        #region Fields

        SharpMap.Wpf.Rendering.VectorRenderer _vectorRenderer = null;
        
        
        FeatureRenderer _featureRenderer = null;
        
        
        MapLayerViewModel _renderedObjects ;
        MapLayerViewModel _renderedSelectedObjects;

   
        MapLayerViewModel _renderedHighlightedObjects;
 
        MapLayerViewModel _renderedAdorners;


        ObservableCollection<MapLayerViewModel> _rederedLayers = new ObservableCollection<MapLayerViewModel>();
        public ObservableCollection<MapLayerViewModel> RederedLayers
        {
            get
            {
                return _rederedLayers;
            }
            private set
            {
                _rederedLayers = value;
            }
        }
 
        bool _renderingSelection = false;
        #endregion

        //-----------------------------------------------------
        //
        //  Layer Selection Refresh Workaournd
        //
        //-----------------------------------------------------
        
        // TODO: this is a temporary solution to the fact that when selected/highlighted layer
        // chages and it contains no features, Render is not called...
       
        #region Layer Selection Refresh Workaournd

        private void wireupExistingLayers(IEnumerable<ILayer> layerCollection)
        {
            foreach (ILayer layer in layerCollection)
            {
                if (layer is IFeatureLayer)
                {
                    IFeatureLayer featureLayer = layer as IFeatureLayer;
                    Debug.Assert(featureLayer != null);
                    wireupLayer(featureLayer);
                }
            }
        }

        void layersListChanged(object sender, ListChangedEventArgs e)
        {
            switch (e.ListChangedType)
            {
                case ListChangedType.ItemAdded:
                    if (Map.Layers[e.NewIndex] is IFeatureLayer)
                    {
                        IFeatureLayer layer = Map.Layers[e.NewIndex] as IFeatureLayer;
                        wireupLayer(layer);
                    }
                    break;
                case ListChangedType.ItemDeleted:
                    // LayerCollection defines an e.NewIndex as -1 when an item is being 
                    // deleted and not yet removed from the collection.
                    if (e.NewIndex < 0)
                    {
                        IFeatureLayer layer = Map.Layers[e.OldIndex] as IFeatureLayer;
                        if (layer != null)
                        {
                            unwireLayer(layer);
                        }
                    }
                    break;
                case ListChangedType.Reset:
                    wireupExistingLayers(Map.Layers);
                    break;
                default:
                    break;
            }


        }

        private void unwireLayer(IFeatureLayer layer)
        {
            layer.SelectedFeatures.ListChanged -= handleSelectedFeaturesListChangedWorkaround;
            layer.HighlightedFeatures.ListChanged -= handleHighlightedFeaturesListChangedWorkaround;

        }

        private void wireupLayer(IFeatureLayer layer)
        {
            layer.SelectedFeatures.ListChanged += handleSelectedFeaturesListChangedWorkaround;
            layer.HighlightedFeatures.ListChanged += handleHighlightedFeaturesListChangedWorkaround;
        }

        private void handleSelectedFeaturesListChangedWorkaround(object sender, ListChangedEventArgs e)
        {
            IFeatureLayer layer = getLayerFromFeatureDataView(sender, true);
            if (layer != null)
            {
                 RenderedSelectedObjects.Clear();
                 RenderFeatures(GetRenderer<IFeatureRenderer>(layer),
                     (VectorStyle)layer.Style, layer.SelectedFeatures, RenderState.Selected);
            }
        }

        private void handleHighlightedFeaturesListChangedWorkaround(object sender, ListChangedEventArgs e)
        {
            IFeatureLayer layer = getLayerFromFeatureDataView(sender, false);
            if (layer != null)
            {
                RenderedHighlightedObjects.Clear();
                RenderFeatures(GetRenderer<IFeatureRenderer>(layer),
                    (VectorStyle)layer.Style, layer.HighlightedFeatures, RenderState.Highlighted);
            }
        }

        private IFeatureLayer getLayerFromFeatureDataView(object view, Boolean getSelectedView)
        {
            foreach (ILayer layer in Map.Layers)
            {
                if (layer is IFeatureLayer)
                {
                    SharpMap.Data.FeatureDataView compareView = getSelectedView
                                                      ? (layer as IFeatureLayer).SelectedFeatures
                                                      : (layer as IFeatureLayer).HighlightedFeatures;

                    if (ReferenceEquals(compareView, view))
                    {
                        return layer as IFeatureLayer;
                    }
                }
            }

            return null;
        }


        #endregion //Layer Selection Refresh Workaournd
    }



    
}
